﻿using System.Linq.Expressions;
using Shop.Model.Category;
using Shop.Model.DB;
using Shop.Model.FrontCategory;
using Shop.RemoteModel.FrontCategory.Model;
using Shop.Repository;
using SqlSugar;
using WeDonekRpc.Client;
using WeDonekRpc.Helper;

namespace Shop.Collect.lmpl
{
    internal class FrontCategoryCollect : IFrontCategoryCollect
    {
        private readonly IFrontCategoryRepository _Repository;

        public FrontCategoryCollect (IFrontCategoryRepository repository)
        {
            this._Repository = repository;
        }
        public FrontCategoryData[] GetAll ()
        {
            return this._Repository.GetAll<FrontCategoryData>();
        }
        public long[] GetSubId (DBFrontCategory category)
        {
            string level = category.Lvl == 1 ? "|" + category.Id + "|" : category.LevelCode + category.Id + "|";
            return this._Repository.Gets(a => a.LevelCode.StartsWith(level), a => a.Id);
        }

        public void Delete (DBFrontCategory category, long[] subId)
        {
            this._Repository.Delete(subId.Add(category.Id));
        }
        public void Stop (DBFrontCategory category)
        {
            long[] subId = this.GetSubId(category);
            this._Repository.Stop(subId.Add(category.Id));
        }
        public void Enable (DBFrontCategory category)
        {
            if (category.Lvl == 1)
            {
                this._Repository.Enable(new long[] { category.Id });
            }
            else
            {
                string code = category.LevelCode;
                long[] ids = code.Remove(code.Length - 1, 1).Remove(0, 1).SplitToLong('|');
                this._Repository.Enable(ids.Add(category.Id));
            }
        }
        public DBFrontCategory Get (long id)
        {
            return this._Repository.Get(id);
        }
        public Result Get<Result> (long id) where Result : class
        {
            return this._Repository.Get<Result>(id);
        }
        public Result Get<Result> (long id, Expression<Func<DBFrontCategory, Result>> select)
        {
            return this._Repository.Get<Result>(id, select);
        }

        public Result[] Gets<Result> (FrontCategoryGet param) where Result : class, new()
        {
            return this._Repository.Gets<Result>(param);
        }
        public bool Set (DBFrontCategory source, FrontCategorySet set)
        {
            if (source.IsEnable)
            {
                throw new ErrorException("shop.front.category.already.enable");
            }
            else if (source.CategoryName != set.CategoryName && this._Repository.IsExist(a => a.ParentId == source.ParentId && a.CategoryName == set.CategoryName))
            {
                throw new ErrorException("shop.front.category.name.repeat");
            }
            return this._Repository.Update(source, set);
        }
        public long Add (FrontCategoryAdd add)
        {
            if (this._Repository.IsExist(a => a.ParentId == add.ParentId && a.CategoryName == add.CategoryName))
            {
                throw new ErrorException("shop.front.category.name.repeat");
            }
            DBFrontCategory category = add.ConvertMap<FrontCategoryAdd, DBFrontCategory>();
            category.Sort = this._Repository.Max<int>(a => a.ParentId == add.ParentId, a => a.Sort);
            category.Sort += 1;
            category.Lvl = 1;
            if (add.ParentId != 0)
            {
                var prt = this._Repository.Get(add.ParentId, c => new
                {
                    c.LevelCode,
                    c.Lvl
                });
                category.Lvl = (short)( prt.Lvl + 1 );
                if (prt.LevelCode == string.Empty)
                {
                    category.LevelCode = "|" + add.ParentId + "|";
                }
                else
                {
                    category.LevelCode = prt.LevelCode + add.ParentId + "|";
                }
            }
            this._Repository.Add(category);
            return category.Id;
        }

        public void Move (DBFrontCategory source, DBFrontCategory to)
        {
            CategoryMoveArg arg = new CategoryMoveArg();
            int toLvl = 1;
            string levelCode;
            if (to == null)
            {
                levelCode = "|" + source.Id + "|";
                arg.Lvl = 1;
                arg.ParentId = 0;
                arg.Sort = this._Repository.Max(c => c.ParentId == 0, a => a.Sort);
                arg.Sort += 1;
                arg.LevelCode = string.Empty;
            }
            else
            {
                toLvl = to.Lvl;
                arg.Lvl = to.Lvl + 1;
                arg.ParentId = to.Id;
                arg.Sort = this._Repository.Max(c => c.ParentId == to.Id, a => a.Sort);
                arg.Sort += 1;
                if (to.Lvl == 1)
                {
                    arg.LevelCode = "|" + to.Id + "|";
                }
                else
                {
                    arg.LevelCode = to.LevelCode + to.Id + "|";
                }
                levelCode = arg.LevelCode + source.Id + "|";
            }
            string level = source.LevelCode == string.Empty ? "|" + source.Id + "|" : source.LevelCode + source.Id + "|";
            var sub = this._Repository.Gets(a => a.LevelCode.StartsWith(level), a => new
            {
                a.Id,
                a.LevelCode,
                a.Lvl
            });
            SubCategorySet[] sets = null;
            if (!sub.IsNull())
            {
                int lvl = toLvl - source.Lvl;
                sets = sub.ConvertAll(c =>
                {
                    return new SubCategorySet
                    {
                        Id = c.Id,
                        LevelCode = levelCode + c.LevelCode.Substring(level.Length),
                        Lvl = c.Lvl + lvl,
                    };
                });
            }

            this._Repository.Move(source.Id, arg, sets);
        }

        public Dictionary<long, int> SetSort (DBFrontCategory category, int sort)
        {
            int num = category.Sort - sort;
            Dictionary<long, int> sorts = new Dictionary<long, int>()
            {
               {category.Id,sort }
            };
            if (Math.Abs(num) == 1)
            {
                long id = this._Repository.Get(a => a.ParentId == category.ParentId && a.Sort == sort, a => a.Id);
                if (id != 0)
                {
                    sorts.Add(id, category.Sort);
                }
            }
            else
            {
                int begin = category.Sort;
                int end = sort;
                if (begin > end)
                {
                    begin = sort;
                    end = category.Sort - 1;
                }
                else
                {
                    begin += 1;
                }
                var datas = this._Repository.Gets(a => a.ParentId == category.ParentId && SqlFunc.Between(a.Sort, begin, end), a => new
                {
                    a.Id,
                    a.Sort
                }).OrderBy(a => a.Sort).ToArray();
                if (!datas.IsNull() && datas[0].Sort == sort)
                {
                    num = num > 0 ? 1 : -1;
                    for (int i = 0; i < datas.Length; i++)
                    {
                        int next = i + 1;
                        var t = datas[i];
                        int s = t.Sort + num;
                        sorts.Add(t.Id, s);
                        if (next != datas.Length && datas[next].Sort != s)
                        {
                            break;
                        }
                    }
                }
            }
            this._Repository.SetSort(sorts);
            return sorts;
        }

        public string[] GetFullName (long frontId)
        {
            var front = this._Repository.Get(a => a.Id == frontId, a => new
            {
                a.LevelCode,
                a.CategoryName
            });
            if (front.LevelCode == string.Empty)
            {
                return new string[]
                {
                    front.CategoryName
                };
            }
            long[] ids = front.LevelCode.Substring(1, front.LevelCode.Length - 2).SplitToLong('|');
            return this._Repository.Gets(ids, a => a.CategoryName).Add(front.CategoryName);
        }
    }
}
